classdef ParseRawSBE16pData < ParseRawSBEData
  % Parser for raw SBE-16+ data
  % Derived from ParseRawSBEData
  % C-tor sig: p = ParseRawSBE25Data(dataFilename, xmlconFilename); 
  %                args optional
  %    dataFilename: path of hex data file
  %    xmlconFilename: path of xmlcon file
  %
  % Author: David Goldsmith, Wash. State Dept. of Ecology, dgol461@ecy.wa.gov
  % Release date: NA
  %
  % An OO implemetation--based on the SBE-16+ documentation, with parts 
  % borrowed from IMOS' SBE Parsers--of the Sea-Bird hexadecimal raw data 
  % translation protocol quoted below.
  %
  % From the "SBE 16plus SEACAT w/ RS-232 Interface User's Manual, Version
  % #014, 11/2/2005," pg. 46 (abridged to those portions we use):
  %
  % Temperature: A/D counts = tttttt
  % Conductivity: conductivity frequency (Hz) = cccccc / 256
  % Pressure: A/D counts = pppppp
  % Temperature compensation (of Pressure): voltage = vvvv / 13,107
  % External voltages 0-3: each vvvv / 13,107
  % Time: seconds since January 1, 1980 = ssssssss

    properties 
      % inherited from ParseData
      %     dataFilename
      % inherited from ParseRawSBEData
      %     xmlconFilename  = ''; full (perhaps relative) path of CTD xmlcon file
      %     instHeaderLines = {}; cell array of strings, from the header, containing instrument data
      %                           in the file these lines are preceded by a '*'
      %     procHeaderLines = {}; cell array of strings, from the header, containing processed data
      %                           in the file these lines are preceded by a '#'
      %     dataLines       = {}; cell array of strings, from the "body" of the file, 
      %                           containing the raw data; these lines are
      %                           preceded by neither of the above
        order = 0;
        Sensors = struct;
        instHdrFldTokMap = struct(...% structure containing mapping of parsing tokens to instHeaderData field names
                                  'tempSN', 'Temperature SN =', ...
                                  'condSN', 'Conductivity SN =', ...
                                  'batteryStatus', 'vbatt =',...
                                  'opCurrent', 'ioper =',...
                                  'extV', 'Volt 0 =');
        Tn; % Temperature
        Cf; % Conductivity
        Pn; % Pressure
        Pv; % Temperature compensation (of Pressure) voltage
        rV; % External Sensors (variable #).
    end

    methods
        function parser = ParseRawSBE16pData(dataFilename, xmlconFilename) % Constructor
            if nargin < 1, dataFilename = ''; end
            if nargin < 2, xmlconFilename = ''; end
            parser = parser@ParseRawSBEData(dataFilename, xmlconFilename);
            if ~isempty(dataFilename), parser.readDataFile(); end
            if ~isempty(xmlconFilename), parser.readConfigFiles(); end
        end
        
        function parseInstHeader(obj)
            global instHeaderData
            instHeaderData.siteID = upper(obj.dataFilename(end-10:end-4));
            flds = fields(obj.instHdrFldTokMap);
            for i=1:length(flds)
                start = regexp(obj.instHeaderLines, obj.instHdrFldTokMap.(flds{i}));
                start = start(1);
                if strcmp(obj.instHdrFldTokMap.(flds{i}), 'Volt 0 =')
                    tmp = obj.instHeaderLines(start:end);
                else
                    finis = regexp(obj.instHeaderLines, obj.instHdrFldTokMap.(flds{i+1}));
                    finis = finis(1) - 1;
                    tmp = obj.instHeaderLines(start:finis);
                end
                tmp = strtrim(strrep(tmp, obj.instHdrFldTokMap.(flds{i}), ''));
                if ~isempty(regexp(obj.instHdrFldTokMap.(flds{i}),...
                                   'Volt [0-3] =', 'ONCE'))
                    tmp = textscan(tmp, '%[yesno], Ext Volt %d = ');
                    val = ~cellfun(@isempty, regexp(tmp{1}, 'y'));
                else
                    val = extract_leading_number(tmp);
                end
                instHeaderData.(flds{i}) = val;
            end
            instHeaderData.Datcnv_date = utcnow();
        end
        
        function parseProcHeader(obj)
            for line = obj.procHeaderLines
            end
        end
        
        function unpackConfigDat(obj)
            global instHeaderData xmlconData
            if isfield(xmlconData, 'Instrument_SensorArray_Sensor') && ...
               iscell(xmlconData.Instrument_SensorArray_Sensor)
                xmlconData.('Instrument_SensorArray') = struct;
                for c=xmlconData.Instrument_SensorArray_Sensor(:)'
                    tmp = c{1};
                    if isstruct(tmp)
                        structs = {}; nstructs = {};
                        fields = fieldnames(tmp);
                        for field=fields(:)'
                            f = char(field);
                            if isstruct(tmp.(f))
                                structs{end+1} = tmp.(f);
                            else
                                nstructs{end+1} = f;
                            end
                        end
                        for n=nstructs(:)'
                            f = char(n);
                            for S=structs(:)'
                                s = S{1};
                                fields = fieldnames(s);
                                if ismember(f, fields) && ...
                                   isequal(tmp.(f), s.(f))
                                    tmp = rmfield(tmp, f);
                                else
                                    s.(f) = tmp.(f);
                                    tmp = rmfield(tmp, f);
                                end
                            end
                        end
                        fields = fieldnames(tmp);
                        f = char(fields{1});
                        xmlconData.Instrument_SensorArray.(f) = s;
                    end
                end
                xmlconData = rmfield(xmlconData, 'Instrument_SensorArray_Sensor');
                fields = fieldnames(xmlconData.Instrument_SensorArray);
                for field=fields(:)'
                    f = char(field);
                    [xmlconData.Instrument_SensorArray.(f),~] = ...
                            flattenStruct({xmlconData.Instrument_SensorArray.(f)});
                end
                obj.Sensors = xmlconData.Instrument_SensorArray;
                xmlconData = rmfield(xmlconData, 'Instrument_SensorArray');
            end
          % Assign values historically obtained from psa files
            instHeaderData.('skip') = 0;
            instHeaderData.('outType') = 'DB';
        end
        
        function convertHexData(obj)
            global instHeaderData 
            N = numel(obj.dataLines);
            extV = instHeaderData.nV;
            hexForm = 'ttttttccccccsppp';
            if extV
                hexForm = [hexForm '0uuu'];
            end
            if extV > 1
                hexForm = [hexForm(1:end-4) hexForm(end-2:end) 'vvv'];
            end
            if extV > 2
                hexForm = [hexForm '0www'];
            end
            if extV > 3
                hexForm = [hexForm(1:end-4) hexForm(end-2:end) 'xxx'];
            end
            if extV > 4
                hexForm = [hexForm '0yyy'];
            end
            if extV > 5
                hexForm = [hexForm(1:end-4) hexForm(end-2:end) 'zzz'];
            end
            if extV > 6
                hexForm = [hexForm '0aaa'];
            end
            obj.Tf = zeros(N,1); obj.Cf = obj.Tf; obj.Pn = obj.Tf;
            obj.rV = zeros(N, extV);
            
            n = 1;
            codes = 'uvwxyza';
            for lin = obj.dataLines
                line = lin{:};
                try
                    tmp = line(hexForm=='t');
                    obj.Tf(n) = 256*hex2dec(tmp(1:2)) + hex2dec(tmp(3:4)) + hex2dec(tmp(5:6))/256;

                    tmp = line(hexForm=='c');
                    obj.Cf(n) = 256*hex2dec(tmp(1:2)) + hex2dec(tmp(3:4)) + hex2dec(tmp(5:6))/256;

                    sgn = 1 - str2double(line(hexForm=='s'))/2;
                    tmp = line(hexForm=='p');
                    obj.Pn(n) = sgn * (256*hex2dec(tmp(1)) + 16*hex2dec(tmp(2)) + hex2dec(tmp(3)));

                    for i=1:extV
                        tmp = line(hexForm==codes(i));
                        obj.rV(n,i) = (256*hex2dec(tmp(1)) + 16*hex2dec(tmp(2)) + hex2dec(tmp(3)))/819;
                    end
                catch
                end
                n = n + 1;
            end
        end
        
    end
end
